#include "HardwareUsart.h"
#include <avr/io.h>



unsigned long F_OSC = 0;
volatile unsigned char usartTransmitFlag;
volatile unsigned char usartReceiveFlag;
volatile unsigned char usartReceivedDataTemp;



void HardwareUsart::setTransmitData(unsigned char data)
{
	UDR = data;
}

unsigned char HardwareUsart::getReceivedData()
{
	return UDR;
}

void HardwareUsart::setBaudDivisor(unsigned char flag)
{
	UCSRA = SetBit(UCSRA, U2X, flag);
}

void HardwareUsart::setMultiProcessorCommunication(unsigned char flag)
{
	UCSRA = SetBit(UCSRA, MPCM, flag);
}

void HardwareUsart::setInterruptOnReceive(unsigned char flag)
{
	UCSRB = SetBit(UCSRB, RXCIE, flag); //sets the interrupt on receive.
}

void HardwareUsart::setInterruptOnSend(unsigned char flag)
{
	UCSRB = SetBit(UCSRB, TXCIE, flag);
}

void HardwareUsart::setInterruptOnDataEmpty(unsigned char flag)
{
	UCSRB = SetBit(UCSRB, UDRIE, flag);
}

void HardwareUsart::setReceiver(unsigned char flag)
{
	UCSRB = SetBit(UCSRB, RXEN, flag);
}

void HardwareUsart::setTransmitter(unsigned char flag)
{
	UCSRB = SetBit(UCSRB, TXEN, flag);
}

void HardwareUsart::setDataSize(unsigned char flag) //set the size using a switch statement
{
	switch(flag)
	{
		case Data_5BitsUSART:
			UCSRC = SetBit(UCSRC, UCSZ0, 0);
			UCSRC = SetBit(UCSRC, UCSZ1, 0);
			UCSRB = SetBit(UCSRB, UCSZ2, 0);
			break;
		case Data_6BitsUSART:
			UCSRC = SetBit(UCSRC, UCSZ0, 1);
			UCSRC = SetBit(UCSRC, UCSZ1, 0);
			UCSRB = SetBit(UCSRB, UCSZ2, 0);
			break;
		case Data_7BitsUSART:
			UCSRC = SetBit(UCSRC, UCSZ0, 0);
			UCSRC = SetBit(UCSRC, UCSZ1, 1);
			UCSRB = SetBit(UCSRB, UCSZ2, 0);
			break;
		case Data_8BitsUSART:
			UCSRC = SetBit(UCSRC, UCSZ0, 1);
			UCSRC = SetBit(UCSRC, UCSZ1, 1);
			UCSRB = SetBit(UCSRB, UCSZ2, 0);
			break;
		case Data_9BitsUSART:
			UCSRC = SetBit(UCSRC, UCSZ0, 1);
			UCSRC = SetBit(UCSRC, UCSZ1, 1);
			UCSRB = SetBit(UCSRB, UCSZ2, 1);
			break;
		default: //assume 8 bits.
			UCSRC = SetBit(UCSRC, UCSZ0, 1);
			UCSRC = SetBit(UCSRC, UCSZ1, 1);
			UCSRB = SetBit(UCSRB, UCSZ2, 0);		
	}
}

void HardwareUsart::setURSEL_Register(unsigned char flag)
{
	UBRRH = SetBit(UBRRH, 15, flag);
	UCSRC = SetBit(UCSRC, 7, flag);
}

void HardwareUsart::setAsyncMode()
{
	UCSRC = SetBit(UCSRC, UMSEL, 0); //async == 0
}

void HardwareUsart::setSyncMode()
{
	UCSRC = SetBit(UCSRC, UMSEL, 1); //sync == 1
}
void HardwareUsart::setUBRR(unsigned long int flag)
{
	//UBRR = flag;
	UBRRL = (unsigned char) (0x00FF & flag);
	UBRRH = (unsigned char) ((flag >> 8) & 0x00FF);
}
void HardwareUsart::setParity(unsigned char flag)
{
	switch(flag)
	{
		case DisabledParityHardwareUSART:
			UCSRC = SetBit(UCSRC, UPM1, 0);
			UCSRC = SetBit(UCSRC, UPM0, 0);
			break;
		case ReservedParityHardwareUSART:
			UCSRC = SetBit(UCSRC, UPM1, 0);
			UCSRC = SetBit(UCSRC, UPM0, 1);
			break;
		case EvenParityHardwareUSART:
			UCSRC = SetBit(UCSRC, UPM1, 1);
			UCSRC = SetBit(UCSRC, UPM0, 0);
			break;
		case OddParityHardwareUSART:
			UCSRC = SetBit(UCSRC, UPM1, 1);
			UCSRC = SetBit(UCSRC, UPM0, 1);
			break;
		default:
			break;
	}
}

void HardwareUsart::setStopBit(unsigned char flag)
{//flag 0 == 1 stop bit, 1 == 2 stop bits
	UCSRC = SetBit(UCSRC, USBS, flag);
}

void HardwareUsart::setPolarity(unsigned char flag)
{
	UCSRC = SetBit(UCSRC, UCPOL, flag);
}


unsigned long HardwareUsart::getClosestUBRR(unsigned long rate)
{
	if(GetBit(UCSRA, U2X) == 0 && GetBit(UCSRC, UMSEL) == 0) //async normal mode.
	{	
		unsigned long newUBRR = (F_OSC/(16*rate));
		newUBRR = (newUBRR - 1) & 0x0FFF; 
		return newUBRR;
	}
	else if(GetBit(UCSRA, U2X)==1 && GetBit(UCSRC, UMSEL) == 0) //async double speed mode
	{
		unsigned long newUBRR = (F_OSC/(8*rate));
		newUBRR = (newUBRR - 1) & 0x0FFF; 
		return newUBRR;
	}
	else if(GetBit(UCSRC, UMSEL) == 0) //sync mode
	{
		unsigned long newUBRR = (F_OSC/(2*rate));
		newUBRR = (newUBRR - 1) & 0x0FFF; 
		return newUBRR;
	}
	else //assume normal async mode
	{
		unsigned long newUBRR = (F_OSC/(16*rate));
		//UBRR is only 12 bits long so cut off the last bits.
		newUBRR = (newUBRR - 1) & 0x0FFF; 

		return newUBRR;
	}
}
unsigned long HardwareUsart::getClosestBaudRate(unsigned long rate)
{
	unsigned long newUBRR = getClosestUBRR(rate);
	
	if(GetBit(UCSRA, U2X) == 0 && GetBit(UCSRC, UMSEL) == 0) //async normal mode.
	{	
		//the value for UBRR would be this now reverse calculate the closest baud
		unsigned long closestBaud = (F_OSC/(16*(newUBRR+1)));
		return closestBaud;
	}
	else if(GetBit(UCSRA, U2X)==1 && GetBit(UCSRC, UMSEL) == 0) //async double speed mode
	{ 
		//the value for UBRR would be this now reverse calculate the closest baud
		unsigned long closestBaud = (F_OSC/(8*(newUBRR+1)));
		return closestBaud;
	}
	else if(GetBit(UCSRC, UMSEL) == 0) //sync mode
	{
		//the value for UBRR would be this now reverse calculate the closest baud
		unsigned long closestBaud = (F_OSC/(2*(newUBRR+1)));
		return closestBaud;
	}
	else //assume normal async mode
	{
		//the value for UBRR would be this now reverse calculate the closest baud
		unsigned long closestBaud = (F_OSC/(16*(newUBRR+1)));
		return closestBaud;
	}	
}


double HardwareUsart::setBaudRate(unsigned long rate) //returns the error for the given baud rate after it is set.
{
	unsigned long bestBaudRate = getClosestBaudRate(rate);
	double error = bestBaudRate / (rate*1.0);
	error = error - 1;
	error = error * 100;
	//set the baud rate.
	unsigned long newUBRR = getClosestUBRR(rate);
	UBRRH = (UBRRH & 0xF0) | ((unsigned char)(bestBaudRate>>8));
	UBRRL = (unsigned char)bestBaudRate;

	return error;
}


void HardwareUsart::setCurrentFrequency(unsigned long freq)
{
	F_OSC = freq;
}
void HardwareUsart::configureHardware()
{
	setBaudDivisor(config.baudDivisor);
	setMultiProcessorCommunication(config.multiProcessorCommunication);
	setTransmitData(config.transmitData);
	setInterruptOnReceive(config.receiveInterrupt);
	setInterruptOnSend(config.sendInterrupt);
	setInterruptOnDataEmpty(config.emptyInterrupt);
	setReceiver(config.receiver);
	setTransmitter(config.transmitter);
	setDataSize(config.size);
	setURSEL_Register(config.URSELRegister);
	if(config.sync==0)
		setAsyncMode();
	else
		setSyncMode();
	setParity(config.parity);
	setStopBit(config.stopNum);
	setPolarity(config.polarity);
	setUBRR(config.UBRRRegister);
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
//Public functions



unsigned char HardwareUsart::getEmptyFlag()
{
	return GetBit(UCSRA, UDRE);
}
	


HardwareUsart::HardwareUsart()
{

	config.parity = DisabledParityHardwareUSART;
	config.size = Data_8BitsUSART;
	config.sync = 0; //async
	config.receiveInterrupt = 0;
	config.sendInterrupt = 0;
	config.emptyInterrupt = 0;
	config.receiver = 0;
	config.transmitter = 0;
	config.multiProcessorCommunication = 0;
	config.baudDivisor = 0;
	config.transmitData = 0x00;
	config.receivedData = 0x00;
	config.URSELRegister = 0;
	config.stopNum = 0;
	config.polarity = 0;
	config.UBRRRegister = 0x0000;
	configureHardware();

}

HardwareUsart::HardwareUsart(HardwareUsartConfig& c)
{

	config = c;
	configureHardware();

}



void HardwareUsart::set(unsigned char data)
{

	config.transmitData = data;

}

unsigned char HardwareUsart::get()
{

	return config.receivedData;

}

unsigned char HardwareUsart::getReceiveCompleteFlag()
{
	return GetBit(UCSRA, RXC);
}
unsigned char HardwareUsart::getTransmitCompleteFlag()
{
	return GetBit(UCSRA, TXC);
}
bool HardwareUsart::getFinished()
{
	
	if(this->getTransmitCompleteFlag()==1 || this->getReceiveCompleteFlag()==1)
		return true;
	else
		return false;
}


void HardwareUsart::run()
{

	//receive data
	if(UCSRA & (1<<RXC))
	{
		unsigned char r = getReceivedData();
		config.receivedData = r;
	}
	if(UCSRA & (1<<UDRE))
	{
		//send new data
		setTransmitData(config.transmitData);
	}

}

/*****

ISR(USART_TXC_vect)
{
	usartTransmitFlag=1;
}
ISR(USART_RXC_vect)
{
	usartReceivedDataTemp = UDR;
	usartReceiveFlag = 1;
}



*****/





